=begin
#  iCam in Ruby (minimal implementation) for Android
###############################################################################
# Works with Ruboto v0.8
# Although really NOT recommended, for Ruboto v0.7 I have a deprecated version
#     of iCamTest.rb you can use
# !!!!See Z:\1PhD\ReVival\Android\Ruby\11Examples\Ruboto\demo-camera_Ruboto_v0_8_1.rb for differences
#
###############################################################################
#To run: 
#    - install Ruboto on Android (I did not try on jRuby on Android, since it's no longer active)
#    - copy script in the script folder (normally /sdcard/jruby) of the Android device
#    - run Ruboto IRB and execute the script.
#To stop the application:
#    - simply pressing the Home or Return button stops the Viewfinder (hence the JPEG saving), but doesn't stop the audio capturing.
#######################################################
# Author: mostly Alex Susu.
#   (started from demo-camera.rb example by Scott Moyer.)
# 
# What the script does:
# 1. starts the ViewFinder
# 2. the ViewFinder frame stream is analyzed with MD - with name related to the time taken, audio amplitude and, if motion detected, average R&G&B deltas.
#   We perform simple motion detection by computing average R&G&B deltas from the previous frame for 96 pixels of the frame.
#    The neighboring pixels are equally distanced from one another on X and Y with the $CONVERT_DELTA_X, respectively $CONVERT_DELTA_Y global constants.
#    Unfortunately, Ruboto runs slow, so we have to process few pixels to achieve the task of Motion detection of ~400ms.
#   We save the interesting JPEGs and the following ones for $FOR_HOW_LONG_WE_ARE_ALARMED seconds from the moment of detection in the folder $LOCAL_FOLDER_MEDIA_FILES.
#   The naming convention of the JPEG is the following: year_mo_da_hh_mm_ss_msc_maxAm_rde_gde_bde.jpg for the first frame with interesting event in it and for the following year_mo_da_hh_mm_ss_msc_maxAm.jpg for the next $FOR_HOW_LONG_WE_ARE_ALARMED seconds.
        This makes lexicographic order of JPEG filenames = chronological order of creation of JPEG files.
# 3. does audio rec in parallel to frame capturing and saving with name related to the time taken
# 4. reads and prints the maximum audio amplitude over the last time window
# 5. all printed info is saved in log files in $LOCAL_FOLDER
#
#######################################################
# Currently tested on Prestigio tablet model PMP7074B 3G.
# TODO: VERY IMPORTANT:
#     !!!!Do noise reduction (or SNR) - because tablet starts having big noise on audio, which only goes away if rebooting the device.
#       for example, G:\iCam\Media\2012_09_16_00\11366_2012_09_16_00_01_37_932.amr contains big noise
#         G:\iCam\Media\2012_09_16_00 - volume always bigger than 11,000 - so it continuously recorded JPEGs as well
# TODO: Read from config file: min space on sdcard to allow application to start, various constants used in the program
# TODO: 
# TODO: More complex detection: 
    - video motion detection using OpenCV, running natively, Android NDK;
    - more complex audio than just max axmplitude - e.g, based on FFT audio analysis and
    Note that Mary at taburet - small avg delta R,G,B which normally wouldn't have triggered the alarm, but the alarm was triggered because of audio - "Z:\1PhD\ReVival\Logs\Prestigio_PMP7074\2012_08_26\!12700_2012_08_26_18_34_57_233_002_000_001.jpg"

  Vary $myDeltaX and $myDeltaY depending on: audio amplitude, etc
        - in fact we should vary more often $myDeltaX and $myDeltaY a little in order to cover more pixels per second
    - make $myDeltaX and $myDeltaY very small until something is discovered and then make them bigger for a while

# TODO: handle properly the aspect ratio of the viewfinder and rotation - see code at https://github.com/ruboto/ruboto/wiki/Tutorial%3A-Making-a-spycam .
# TODO: in case of alert make the phone call work well
# TODO: use webrick to have a web server to serve images - reuse existing example from https://github.com/ruboto/ruboto/wiki/Tutorial%3A-Making-a-spycam.
# TODO: Handle other types of Android devices better - maybe some don't handle NV21 (also Android less than 2.1 SDK - must implement conversion NV21->RGB) etc
# TODO: Send to the Internet: Picasa, YouTube, iCam server.
# TODO:
    * Method names should start with a lower case character.
    * If a rescue block covers the entire method, you don't need "begin" and "end"
    * Ruby uses two spaces per indentation level.  Java uses 4 spaces.
    * You should investigate running the script as a stand-alone app. 
#All/most of the interesting samples are in Z:\1PhD\ReVival\Android\Ruby\Ruboto\1\org.ruboto.irb-1.apk
#    \assets\demo-scripts\demo-camera.rb
#    Note: classes.dex - contains org.ruboto.callbacks.RubotoSurfaceHolderCallback
#
#From some webpage - they were telling you can generate callbacks:
#    "ruboto gen interface android.view.SurfaceHolder.Callback --name RubotoSurfaceHolderCallback"
=end


require 'ruboto/activity'
require 'ruboto/util/toast'

import android.view.Surface

import "android.content.Intent"
import "android.net.Uri"


java_import "android.os.StatFs"

java_import "android.view.SurfaceView"
java_import "android.hardware.Camera"

java_import "android.graphics.YuvImage"
java_import "android.graphics.Rect"
java_import "android.graphics.ImageFormat"
#Note that we don't do: import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback;

java_import "android.media.MediaRecorder"
#java_import "android.media.MediaRecorder.VideoSource" #gives error: "=> NameError: cannot load Java class"
#java_import "android.media.MediaRecorder.AudioSource" #gives error: "=> NameError: cannot load Java class"

#java_import "android.hardware.Camera.PreviewCallback"
#Gives error: 
#=> NameError: cannot load Java class
#android.hardware.Camera.PreviewCallback
#      for_name at org/jruby/javasupport/JavaClass.java:1207
#  get_proxy_class at org/jruby/Javasupport/JavaUtilities.java:34
#    java_import at file:/data/app/org.ruboto.core-1.apk/builtin/
#javasupport/core_ext/object.rb:46
#  (root) at demo-camera.rb:16
#nil

#Note that we don't import the java.io package

#Good for Ruboto 0.7, BUT Not working on Ruboto v0.8: ruboto_import "org.ruboto.callbacks.RubotoSurfaceHolderCallback"



#$LOCAL_FOLDER = "#{Dir.pwd}"
#$LOCAL_FOLDER = "./"
#$LOCAL_FOLDER = "/sdcard/iCam"
#$LOCAL_DRIVE = "/sdcard/extsd" # For Prestigio tablet
$LOCAL_DRIVE = "/sdcard/external_sd" # For Samsung GalaxyS

$LOCAL_FOLDER = $LOCAL_DRIVE + "/iCam"
$LOCAL_FOLDER_MEDIA_FILES = $LOCAL_FOLDER + "/Media"
#$LOCAL_FOLDER_MEDIA_AUDIO_FILES = $LOCAL_FOLDER_MEDIA_FILES + "/Audio"

#Global variables
$myCounter = 0 
$mPreviewSize = nil
$maxAmplitudeSoundLastRecording = 0

$JPEGfileSizeLastPhoto = 0
$JPEGfileSizeVariation = 0.0
#$maxAmplitudeSoundLastRecordingUpdatable = true #No longer used, since I do in a single thread RecordAudioStart() and RecordAudioStop().

$interestingEventDetected = false
#END Global variables
$interestingEventDetectedNumTimes = 0

$USE_JPEG_FILESIZE = false #true
$RGB_THRESHOLD_TO_CONSIDER_MOTION = 5
$FOR_HOW_LONG_WE_ARE_ALARMED = 10 #[seconds] (this can mean ~19 frames per event, but this depends)
$lastTimeInterestingEventDetected = Time.new() - $FOR_HOW_LONG_WE_ARE_ALARMED - 5 #We put a -5 to be conservative, the first time.


def CreateDirectoriesAndLogFiles
    begin
        #Create $LOCAL_FOLDER and $LOCAL_FOLDER_MEDIA_FILES folders if they don't exist
        if (FileTest.directory?($LOCAL_FOLDER) == false)
            puts "Creating folder %s." % $LOCAL_FOLDER
            Dir.mkdir($LOCAL_FOLDER)
        end

        if (FileTest.directory?($LOCAL_FOLDER_MEDIA_FILES) == false)
            puts "Creating folder %s." % $LOCAL_FOLDER_MEDIA_FILES
            Dir.mkdir($LOCAL_FOLDER_MEDIA_FILES)
        end

        #if (FileTest.directory?($LOCAL_FOLDER_MEDIA_AUDIO_FILES) == false)
        #    puts "Creating folder %s." % $LOCAL_FOLDER_MEDIA_AUDIO_FILES
        #    Dir.mkdir($LOCAL_FOLDER_MEDIA_AUDIO_FILES)
        #end

        time = Time.new()
        #Using STDDateTime (name that starts with captial letter) gives error: "dynamic constant assignment"
        stdDateTime = "%04d_%02d_%02d_%02d_%02d_%02d_%03d" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

        $stdout.reopen($LOCAL_FOLDER + "/" + "stdout_" + stdDateTime + ".txt", "w")
        $stderr.reopen($LOCAL_FOLDER + "/" + "stderr_" + stdDateTime + ".txt", "w")
    rescue => exc
        #puts exc.message
        #p exc.message
        warn exc.message()
        #puts exc.backtrace
        #p exc.backtrace
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end    

CreateDirectoriesAndLogFiles()


def GetFreeDriveSpace(aDriveStr)
    #From http://stackoverflow.com/questions/4595334/android-get-free-space-on-internal-memory:
    #  StatFs stats = new StatFs(aDriveStr)
    #  int availableBlocks = stats.getAvailableBlocks();
    #  int blockSizeInBytes = stats.getBlockSize();
    #  int freeSpaceInBytes = availableBlocks * blockSizeInBytes;

    stats = StatFs.new(aDriveStr)
    availableBlocks = stats.getAvailableBlocks()
    blockSizeInBytes = stats.getBlockSize()
    freeSpaceInBytes = availableBlocks * blockSizeInBytes

    return freeSpaceInBytes
end


def ImportOpenCV
    begin
        java_import "org.opencv"
        java_import "org.opencv.core" #I get: cannot load Java class org.opencv.core
        #org/jruby/javasupport/JavaClass.java:1207:in `for_name'org/jruby/javasupport/JavaUtilities.java:34:in `get_proxy_class'file:/data/app/org.ruboto.core-1.apk!/builtin/javasupport/core_ext/object.rb:46:in `java_import'demo-camera.rb:77:in `(root)'

        #java_import "org.opencv.core.Core" #I get: NameError: cannot load java class org.opencv.core.Core
        #    for_name at org/jruby/javasupport/JavaClass.java:1207
        # get_proxy_class at org/jruby/javasupport/JavaUtilities.java:34

        #java_import "org.opencv.highgui.VideoCapture"
        #java_import "org.opencv.imgproc.Imgproc"
        java_import "org.opencv.core.Mat"
    rescue => exc
        #puts exc.message
        #p exc.message
        warn exc.message()
        #puts exc.backtrace
        #p exc.backtrace
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end



class Camera
    def picture_id
        @picture_id ||= 0
        @picture_id += 1
    end
end


#See https://developer.android.com/reference/android/view/SurfaceView.html: it has public SurfaceHolder getHolder()
#Also, SurfaceHolder has: SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed()
# For Ruboto v 0.8:
class RubotoSurfaceHolderCallback
    attr_reader :camera

    def surfaceCreated(holder)
        begin
            #Using toast() here gives exception: undefined method `toast' for #<Java::OrgRubotoCallbacks::RubotoSurfaceHolderCallback:0x2bca5f50>.

            #MyOpenCV()

            @camera = Camera.open(0) # 0 for the Main camera
            #$camera = Camera.open(1) # 1 for the Front camera
            @camera.preview_display = holder #In Java we have public final void setPreviewDisplay (SurfaceHolder holder) (see https://developer.android.com/reference/android/hardware/Camera.html)

            SetCameraParameters(@camera)

            @camera.start_preview

            #See https://developer.android.com/reference/android/hardware/Camera.html:
            #See also https://developer.android.com/reference/android/hardware/Camera.PreviewCallback.html:
            #$camera.setOneShotPreviewCallback()
            #$camera.preview_callback = mPreviewCallback #The Java method name is setPreviewCallback()
            #$camera.preview_callback = PreviewCallback.new(self) #ArgumentError: wrong number of arguments (1 for 0)
            @camera.preview_callback = PreviewCallback.new()

            ContinuousRecordAudioThread()

            #sleep(5.0)
            #$camera_demo.PhoneCall() #undefined method `PhoneCall' for #<Java::OrgRuboto::RubotoActivity:0x2bd9bcd8 @initialized=true>
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end
    end

    def surfaceChanged(holder, format, width, height)
    end

    def surfaceDestroyed(holder)
        begin
            puts "Entered on_surface_destroyed()."
            $stdout.flush()

            @camera.stop_preview()
            @camera.release()
            @camera = nil
        rescue => exc
            #puts exc.message
            #p exc.message
            warn exc.message()
            #puts exc.backtrace
            #p exc.backtrace
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end
    end
end



$mMediaRecorder = nil
#$mMediaRecorder = MediaRecorder.new() #android.media.MediaRecorder.MediaRecorder()

def RecordAudioStart()
    begin
        puts "Entered RecordAudioStart()."
        $stdout.flush()

        time = Time.new() #IMPORTANT: This must not be global. At every time poll we must give a new() (it's content is not volatile). When I put the var time as global the function SaveFrame() was no longer called after the first use.

        begin
            #Because there is another thread (SaveFrame) that can also mkdir the same folder at the same time, it can give exception like "Unknown error - mkdir failed" "org/jruby/RubyDir.java:574:in `mkdir'iCamTest.rb:463:in `SaveFrame'iCamTest.rb:555:in `onPreviewFrame'"
            crtLOCAL_FOLDER_MEDIA_FILES = $LOCAL_FOLDER_MEDIA_FILES + "/" + "%04d_%02d_%02d_%02d" % [time.year, time.month, time.day, time.hour]
            if (FileTest.directory?(crtLOCAL_FOLDER_MEDIA_FILES) == false)
                Dir.mkdir(crtLOCAL_FOLDER_MEDIA_FILES)
            end
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end

        #imgFileName = "#{Dir.pwd}/img"
        pathFileName = crtLOCAL_FOLDER_MEDIA_FILES + "/" #+ "/aud"
        pathFileName += "%05d_%04d_%02d_%02d_%02d_%02d_%02d_%03d.amr" % [$maxAmplitudeSoundLastRecording, time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

        #$maxAmplitudeSoundLastRecordingUpdatable = false #We can't update call get_max_amplitude() until we don't give .start()
        #sleep(0.1) #We sleep in order to avoid the case the other thread with PreviewCallback does a get_max_amplitude() - checks $maxAmplitudeSoundLastRecordingUpdatable before it becomes false but does the get_max_amplitude() after the instructions below start executing (in part)

        $mMediaRecorder = MediaRecorder.new() #android.media.MediaRecorder.MediaRecorder()

        #Some of these functions makes the Preview stop the second time they are used.
        $mMediaRecorder.set_audio_source(android.media.MediaRecorder::AudioSource::MIC)

        #From https://developer.android.com/reference/android/media/MediaRecorder.OutputFormat.html
        #    int AAC_ADTS    AAC ADTS file format
        #    int AMR_NB  AMR NB file format
        #    int AMR_WB  AMR WB file format
        #    int DEFAULT     
        #    int MPEG_4  MPEG4 media file format
        #    int RAW_AMR     This constant is deprecated. Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB
        #    int THREE_GPP   3GPP media file format
        $mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::DEFAULT)
        #$mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::THREE_GPP) #similar with DEFAULT

        #$mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::AMR_NB) #a bit lower quality, but convertable to Wav using VLC.
        #Similar to AMR_NB
        #$mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::RAW_AMR)

        #It seems it is not supported on Prestigio - all files are 0 sized, and max amplitude = 0, even after a restart.
        #Gives: "java.lang.RuntimeException: start failed." and "java.lang.IllegalStateException: null".
        #$mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::AMR_WB)


        #It seems it is not supported on Prestigio - all files are 0 sized, and max amplitude = 0, even after a restart.
        #It fails silently and it even blocks the viewfinder.
        #$mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::AAC_ADTS)

        #From https://developer.android.com/reference/android/media/MediaRecorder.AudioEncoder.html
        #int     AAC     AAC Low Complexity (AAC-LC) audio codec
        #int     AAC_ELD     Enhanced Low Delay AAC (AAC-ELD) audio codec
        #int     AMR_NB  AMR (Narrowband) audio codec
        #int     AMR_WB  AMR (Wideband) audio codec
        #int     DEFAULT     
        #int     HE_AAC  High Efficiency AAC (HE-AAC) audio codec 
        $mMediaRecorder.set_audio_encoder(android.media.MediaRecorder::AudioEncoder::DEFAULT)

        $mMediaRecorder.set_output_file(pathFileName) #file.getAbsolutePath()

        $mMediaRecorder.prepare()

        $mMediaRecorder.start()

        #$maxAmplitudeSoundLastRecordingUpdatable = true #!!!!Maybe put it after set_audio_source()

        if (true)
        #if (false)
            #Preparing for computing max - the read is done in RecordAudioStop()
            #We need to call get_max_amplitude() after start(), otherwise start() messes up things and the second call to get_max_amplitude() will return invariably also 0.
            #From https://developer.android.com/reference/android/media/MediaRecorder.html#getMaxAmplitude%28%29: 
            #  "Call this only after the setAudioSource()."
            #  "Returns the maximum absolute amplitude measured since the last call, or 0 when called for the first time"
            $maxAmplitudeSoundLastRecording = $mMediaRecorder.get_max_amplitude()
            puts "RecordAudioStart(): maxAmplitudeSoundLastRecording = %d." % $maxAmplitudeSoundLastRecording
            $stdout.flush()
        end

        puts "Exiting RecordAudioStart()."
        $stdout.flush()
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end

def RecordAudioStop()
    begin
        #With prints and flush() RecordAudioStop() took 69, 210, 276 ms - see Z:\1PhD\ReVival\Android\Ruby\11Examples\Ruboto\47_JPEG_and_audio

        time = Time.new()
        puts "Entered RecordAudioStop() at %04d_%02d_%02d_%02d_%02d_%02d_%03d." % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]
        #puts "Entered RecordAudioStop()."
        $stdout.flush()

        #if (false)
            $maxAmplitudeSoundLastRecording = $mMediaRecorder.get_max_amplitude()
            puts "RecordAudioStop(): maxAmplitudeSoundLastRecording = %d." % $maxAmplitudeSoundLastRecording
            $stdout.flush()
        #end

        $mMediaRecorder.stop()
        #Since: API Level 1
        #Stops recording. Call this after start(). Once recording is stopped, you will have to configure it again as if it has just been constructed. Note that a RuntimeException is intentionally thrown to the application, if no valid audio/video data has been received when stop() is called. This happens if stop() is called immediately after start(). The failure lets the application take action accordingly to clean up the output file (delete the output file, for instance), since the output file is not properly constructed when this happens.

        #$mMediaRecorder.reset() #Restarts the MediaRecorder to its idle state. After calling this method, you will have to configure it again as if it had just been constructed. 

        $mMediaRecorder.release() #On Prestigio tablet we really require to do this - we can't reuse well the mediarecorder object, so we have to recreate it for each recording, despite the specs...

        time = Time.new()
        puts "Exiting RecordAudioStop() at %04d_%02d_%02d_%02d_%02d_%02d_%03d." % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]
        #puts "Exiting RecordAudioStop()."
        $stdout.flush()
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end


def ContinuousRecordAudioThread()
    begin
        puts "Entered ContinuousRecordAudioThread()."
        $stdout.flush()

        t1 = Thread.new do
            while (true)
                begin
                    RecordAudioStart()

                    puts "In new thread of ContinuousRecordAudioThread()."
                    $stdout.flush()

                    #raise "Exception from thread"

                    #sleep(7)

                    i = 0
                    while (i < 7)
                        i = i + 1
                        #puts i
                        $maxAmplitudeSoundLastRecording = $mMediaRecorder.get_max_amplitude()
                        sleep(1)
                    end

                    RecordAudioStop()

                    #sleep(1) #1, 2 and 3 were OK
                rescue => exc
                    #p exc.message
                    warn exc.message()
                    warn exc.backtrace()
                    #$stdout.flush()
                    $stderr.flush()
                end
            end
        end
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end


def RecordAudio(pathFileName, recordDuration)
    begin
        puts "Entered RecordAudio()."

        mMediaRecorder = MediaRecorder.new() #android.media.MediaRecorder.MediaRecorder()

        mMediaRecorder.set_audio_source(android.media.MediaRecorder::AudioSource::MIC)

        #From https://developer.android.com/reference/android/media/MediaRecorder.OutputFormat.html
        #    int AAC_ADTS    AAC ADTS file format
        #    int AMR_NB  AMR NB file format
        #    int AMR_WB  AMR WB file format
        #    int DEFAULT     
        #    int MPEG_4  MPEG4 media file format
        #    int RAW_AMR     This constant is deprecated. Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB
        #    int THREE_GPP   3GPP media file format
        mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::DEFAULT)

        #From https://developer.android.com/reference/android/media/MediaRecorder.AudioEncoder.html
        #int     AAC     AAC Low Complexity (AAC-LC) audio codec
        #int     AAC_ELD     Enhanced Low Delay AAC (AAC-ELD) audio codec
        #int     AMR_NB  AMR (Narrowband) audio codec
        #int     AMR_WB  AMR (Wideband) audio codec
        #int     DEFAULT     
        #int     HE_AAC  High Efficiency AAC (HE-AAC) audio codec 
        mMediaRecorder.set_audio_encoder(android.media.MediaRecorder::AudioEncoder::DEFAULT)

        mMediaRecorder.set_output_file(pathFileName) #file.getAbsolutePath()

        mMediaRecorder.prepare()

        mMediaRecorder.start()

        sleep(recordDuration)

        mMediaRecorder.stop()
        #Since: API Level 1
        #Restarts the MediaRecorder to its idle state. After calling this method, you will have to configure it again as if it had just been constructed.
        #mMediaRecorder.reset()

        puts "Exiting RecordAudio()."
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end


def RecordVideo(pathFileName, recordDuration)
    begin
        puts "Entered RecordVideo()."

        #//From http://developer.android.com/reference/android/media/MediaRecorder.html
        #
        #public void prepare ()
        #Since: API Level 1
        #
        #Prepares the recorder to begin capturing and encoding data.
        #This method must be called after setting up the desired audio and video sources, encoders, file format, etc., but before start().
        #Throws
        #    IllegalStateException   if it is called after start() or before setOutputFormat().
        #    IOException     if prepare fails otherwise.
        #
        #public void start ()
        #Since: API Level 1
        #Begins capturing and encoding data to the file specified with setOutputFile(). Call this after prepare().
        #    Since API level 13, if applications set a camera via setCamera(Camera), the apps can use the camera after this method call.
        #    The apps do not need to lock the camera again.
        #    However, if this method fails, the apps should still lock the camera back.
        #    The apps should not start another recording session during recording.
        #Throws
        #    IllegalStateException   if it is called before prepare(). 

        mMediaRecorder = MediaRecorder.new() #android.media.MediaRecorder.MediaRecorder()

        #See https://developer.android.com/reference/android/media/MediaRecorder.VideoSource.html
        mMediaRecorder.set_video_source(android.media.MediaRecorder::VideoSource::CAMERA) #android.media.MediaRecorder.VideoSource::CAMERA, nor MediaRecorder.VideoSource::CAMERA don't work
        #undefined method `VideoSource' for Java::AndroidMedia::MediaRecorder:Class
        #demo-camera.rb:330:in `RecordVideo'demo-camera.rb:465:in `take_picture'demo-camera.rb:376:in `on_create'org/jruby/RubyProc.java:269:in `call'org/jruby/RubyProc.java:223:in `call'

        #See https://developer.android.com/reference/android/media/MediaRecorder.AudioSource.html
        #int audioSource = android.media.MediaRecorder.AudioSource.MIC
        #mMediaRecorder.set_audio_source(android.media.MediaRecorder::AudioSource::MIC)
        mMediaRecorder.set_audio_source(android.media.MediaRecorder::AudioSource::DEFAULT)

        mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::DEFAULT)
        #mMediaRecorder.set_output_format(android.media.MediaRecorder::OutputFormat::THREE_GPP)

        mMediaRecorder.set_audio_encoder(android.media.MediaRecorder::AudioEncoder::DEFAULT)
        #mMediaRecorder.set_audio_encoder(android.media.MediaRecorder::AudioEncoder::AMR_NB)

        mMediaRecorder.set_video_encoder(android.media.MediaRecorder::VideoEncoder::DEFAULT)
        #mMediaRecorder.set_video_encoder(android.media.MediaRecorder::VideoEncoder::H263)
        #!!!!mMediaRecorder.setVideoEncoder(android.media.MediaRecorder.VideoEncoder.H264)

        mMediaRecorder.set_output_file(pathFileName) #file.getAbsolutePath()
        #I got the following error: java.lang.IllegalStateException: null
        #android/media/MediaRecorder.java:-2:in `_setOutputFile'android/media/MediaRecorder.java:543:in `prepare'demo-camera.rb:356:in `RecordVideo'demo-camera.rb:469:in `take_picture'demo-camera.rb:380:in `on_create'org/jruby/RubyProc.java:269:in `call'org/jruby/RubyProc.java:223:in `call'
        #java.lang.IllegalStateException: null
        #android/media/MediaRecorder.java:-2:in `_setOutputFile'android/media/MediaRecorder.java:543:in `prepare'untitled.rb:356:in `RecordVideo'untitled.rb:469:in `take_picture'untitled.rb:380:in `on_create'org/jruby/RubyProc.java:269:in `call'org/jruby/RubyProc.java:223:in `call'

        #xSize = 352
        #ySize = 288
        xSize = 640
        ySize = 480
        mMediaRecorder.set_video_size(xSize, ySize)

        mMediaRecorder.set_audio_channels(1) #Since: API Level 8
        mMediaRecorder.set_audio_sampling_rate(8000) #Since: API Level 8

        mMediaRecorder.set_video_frame_rate(10) #15

        #From https://developer.android.com/reference/android/media/MediaRecorder.html:
        #  "Sets a Surface to show a preview of recorded media (video). Calls this before prepare() to make sure that the desirable preview display is set. If setCamera(Camera) is used and the surface has been already set to the camera, application do not need to call this. If this is called with non-null surface, the preview surface of the camera will be replaced by the new surface. If this method is called with null surface or not called at all, media recorder will not change the preview surface of the camera."
        #surface_view = SurfaceView.new(self)
        #mMediaRecorder.set_preview_display(Surface sv)

        mMediaRecorder.set_max_duration(recordDuration * 1000)

        mMediaRecorder.prepare()

        mMediaRecorder.start()
        #I got: "java.lang.RuntimeException: start failed.
        #android/media/MediaRecorder.java:-2:in `start'demo-camera.rb:377:in `RecordVideo'demo-camera.rb:492:in `take_picture'demo-camera.rb:403:in `on_create'org/jruby/RubyProc.java:269:in `call'org/jruby/RubyProc.java:223:in `call'"
        #        java.lang.RuntimeException: start failed.
        #android/media/MediaRecorder.java:-2:in `start'demo-camera.rb:380:in `RecordVideo'demo-camera.rb:497:in `take_picture'demo-camera.rb:408:in `on_create'org/jruby/RubyProc.java:269:in `call'org/jruby/RubyProc.java:223:in `call'

        sleep(recordDuration)

        mMediaRecorder.stop()

        #Since: API Level 1
        #Restarts the MediaRecorder to its idle state. After calling this method, you will have to configure it again as if it had just been constructed.
        #mMediaRecorder.reset()

        mMediaRecorder.release()

        puts "Exiting RecordVideo()."
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end


$CONVERT_DELTA_X = 80 #320 #80 #40 #20 #10 #6 gives (in the equivalent Python script) "IndexError: list assignment index out of range"
$CONVERT_DELTA_Y = 40 #320 #80 #40 #20 #10
def ConvertNV21ToRGB(rgbResult, yuv420sp, width, height)
    begin
        #On PC at ~2GHz, it takes about 5 seconds to perform the conversion from YUV420sp to RGB.

        #puts "Entered ConvertNV21ToRGB()"

        time = Time.new() #IMPORTANT: This must not be global - when I put the var time as global the function SaveFrame() was no longer called after the first use
        myStr = "%04d_%02d_%02d_%02d_%02d_%02d_%03d" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

        puts "Entered ConvertNV21ToRGB() at %s: width = %d, height = %d" % [myStr, width, height]
        puts "ConvertNV21ToRGB(): yuv420sp[0] = %d" % yuv420sp[0]
        puts "ConvertNV21ToRGB(): yuv420sp.length() = %d" % yuv420sp.length()
        #puts "ConvertNV21ToRGB(): yuv420sp =", yuv420sp
        $stdout.flush()
        #puts "Entered ConvertNV21ToRGB(): rgbResult =", rgbResult
        #$stdout.flush()

        frameSize = width * height

        #rgbResult = Matrix.identity(2)
        #puts rgbResult

=begin
    #From http://stackoverflow.com/questions/9242342/how-do-i-generate-a-4-x-5-matrix-full-of-random-numbers-with-ruby-1-8-7
    rows = (0...height).map do
      (0...width).map do
        0x00000000
      end 
    end

    rgbResult = Matrix[rows]
    #puts rgbResult

    #Note that this doesn't work on Ruby 1.8
    #From http://www.ruby-doc.org/stdlib-1.9.3/libdoc/matrix/rdoc/Matrix.html
    #m = Matrix.build(2, 4) {|row, col| col - row }
    #rgbResult = Matrix.build(width, height) {|row, col| 0x00000000}
    #rgbResult = Matrix.build(width, height) {|row, col| 0}
=end

        yp = 0
        for j in (0..(height - 1)).step($CONVERT_DELTA_Y)
            #print "j = %d" % j

            uvp = frameSize + (j >> 1) * width
            u = 0
            v = 0
            for i in (0..(width - 1)).step($CONVERT_DELTA_X)
                #print "i = %d" % i
                #print "yuv420sp[uvp=%d] = %d" % (uvp, ord(yuv420sp[uvp]))
                #print "yuv420sp[yp=%d] = %d" % (yp, ord(yuv420sp[yp]))

                #y = (0xff & ord(yuv420sp[yp])) - 16
                y = (0xff & yuv420sp[yp]) - 16

                if (y < 0)
                    y = 0
                end

                if ((i & 1) == 0)
                    #v = (0xff & ord(yuv420sp[uvp])) - 128
                    v = (0xff & yuv420sp[uvp]) - 128
                    uvp += 1
                    #u = (0xff & ord(yuv420sp[uvp])) - 128
                    u = (0xff & yuv420sp[uvp]) - 128
                    uvp += 1
                    uvp += ($CONVERT_DELTA_X - 2) #* 2
                end

                y1192 = 1192 * y
                r = y1192 + 1634 * v
                g = y1192 - 833 * v - 400 * u
                b = y1192 + 2066 * u

                if (r < 0)
                    r = 0
                elsif (r > 262143)
                    r = 262143
                end

                if (g < 0)
                    g = 0
                elsif (g > 262143)
                    g = 262143
                end

                if (b < 0)
                    b = 0
                elsif (b > 262143)
                    b = 262143
                end

                #print "yp=%d\n" % (yp)
                #rgbResult[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff)
                rgbResult[yp / $CONVERT_DELTA_X] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff)

                yp += $CONVERT_DELTA_X
            end

            #yp += ($CONVERT_DELTA_Y - 1) * width
        end

        time = Time.new() #IMPORTANT: This must not be global - when I put the var time as global the function SaveFrame() was no longer called after the first use
        myStr = "%04d_%02d_%02d_%02d_%02d_%02d_%03d" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

        puts "Exiting ConvertNV21ToRGB() at %s." % myStr
        $stdout.flush()
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end


$rgb0 = nil
$rgb2 = nil
def SaveFrame(crtFrame)
    #From http://www.ruby-doc.org/core-1.9.3/Thread.html
    #If we don't put the thread it basically doesn't save any file
    #t1 = Thread.new do
        #puts  "In new thread"
        #raise "Exception from thread"
    #end

    begin
        #puts "onPreviewFrame: %02d_%02d_%02d_%06d" % [time.hour, time.min, time.sec, time.usec]

        time = Time.new() #IMPORTANT: This must not be global - when I put the var time as global the function SaveFrame() was no longer called after the first use

        begin
            #Because there is another thread (SaveFrame) that can also mkdir the same folder at the same time, it can give exception like "Unknown error - mkdir failed" "org/jruby/RubyDir.java:574:in `mkdir'iCamTest.rb:463:in `SaveFrame'iCamTest.rb:555:in `onPreviewFrame'"
            crtLOCAL_FOLDER_MEDIA_FILES = $LOCAL_FOLDER_MEDIA_FILES + "/" + "%04d_%02d_%02d_%02d" % [time.year, time.month, time.day, time.hour]
            if (FileTest.directory?(crtLOCAL_FOLDER_MEDIA_FILES) == false)
                Dir.mkdir(crtLOCAL_FOLDER_MEDIA_FILES)
            end
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end

        if (false)
            #I guess because of this get_max_amplitude() and the one that happens in RecordAudioStart() the application crashed after 80 secs (Z:\1PhD\ReVival\Android\Ruby\11Examples\Ruboto\59_JPEG_and_audio_max_crash_after_80_secs\) or 7 minutes (Z:\1PhD\ReVival\Android\Ruby\11Examples\Ruboto\60_JPEG_and_audio_max_crash\).
            #From https://developer.android.com/reference/android/media/MediaRecorder.html#getMaxAmplitude%28%29: 
            #  "Call this only after the setAudioSource()."
            #  "Returns the maximum absolute amplitude measured since the last call, or 0 when called for the first time"
            #!!!!Therefore, need to check if the other thread is in the middle of the reinit of the audio, in which case we don't do this op
            #if ($maxAmplitudeSoundLastRecordingUpdatable == true)
                $maxAmplitudeSoundLastRecording = $mMediaRecorder.get_max_amplitude() #https://developer.android.com/reference/android/media/MediaRecorder.html#getMaxAmplitude%28%29: "Returns the maximum absolute amplitude measured since the last call, or 0 when called for the first time"
                #puts "SaveFrame(): maxAmplitudeSoundLastRecording = %d." % $maxAmplitudeSoundLastRecording
                #$stdout.flush()
            #end
        end

        if (time - $lastTimeInterestingEventDetected > $FOR_HOW_LONG_WE_ARE_ALARMED)
            #"%04d_%02d_%02d_%02d" % [time.year, time.month, time.day, time.hour]

            if (($myCounter % 4) == 0)
                ConvertNV21ToRGB($rgb0, crtFrame, $mPreviewSize.width, $mPreviewSize.height)
            elsif (($myCounter % 4) == 2)
                ConvertNV21ToRGB($rgb2, crtFrame, $mPreviewSize.width, $mPreviewSize.height)
            end

            #puts "rgb.length() =", rgb0.length()
            #$stdout.flush()

            #totalDelta = 0
            totalDeltaR = 0
            totalDeltaG = 0
            totalDeltaB = 0
            rgbLength = $rgb0.length()
            for i in (0..(rgbLength - 1))#.step($CONVERT_DELTA_X)
                #puts i
                #puts rgb[i]
                #totalDelta += ($rgb0[i] - $rgb2[i]).abs()
                totalDeltaR += ((($rgb0[i] >> 16) & 0xFF) - (($rgb2[i] >> 16) & 0xFF)).abs()
                totalDeltaG += ((($rgb0[i] >> 8) & 0xFF) - (($rgb2[i] >> 8) & 0xFF)).abs()
                totalDeltaB += (($rgb0[i] & 0xFF) - ($rgb2[i] & 0xFF)).abs()
            end
            #puts "totalDelta = %d" % totalDelta
            puts "totalDeltaR = %d, totalDeltaG = %d, totalDeltaB = %d" % [totalDeltaR, totalDeltaG, totalDeltaB]
            avgDeltaR = totalDeltaR / rgbLength
            avgDeltaG = totalDeltaG / rgbLength
            avgDeltaB = totalDeltaB / rgbLength
            puts "avgDeltaR = %d, avgDeltaG = %d, avgDeltaB = %d" % [avgDeltaR, avgDeltaG, avgDeltaB]
            $stdout.flush()

            motionDetected = false
            #if ( (totalDeltaR > 500) or (totalDeltaG > 500) or (totalDeltaB > 500) )
            if ( (avgDeltaR >= $RGB_THRESHOLD_TO_CONSIDER_MOTION) or (avgDeltaG >= $RGB_THRESHOLD_TO_CONSIDER_MOTION) or (avgDeltaB >= $RGB_THRESHOLD_TO_CONSIDER_MOTION) )
                motionDetected = true
            end

            #if ($myCounter == 30)           
            #if ( ($maxAmplitudeSoundLastRecording > 16000) and ($JPEGfileSizeVariation.abs() > 0.02) ) #very few - Z:\1PhD\ReVival\Logs\Prestigio_PMP7074\2012_08_22_3\stdout_2012_08_22_18_32_30_764.txt
            #if ( ($maxAmplitudeSoundLastRecording > 16000) and ($JPEGfileSizeVariation.abs() > 0.01) ) #not that many either - see Z:\1PhD\ReVival\Logs\Prestigio_PMP7074\2012_08_23\stdout_2012_08_23_10_24_18_747.txt
            #if ( motionDetected or ( ($maxAmplitudeSoundLastRecording > 11000) and ($JPEGfileSizeVariation.abs() > 0.01) ) ) #not that many either
            #if ( motionDetected or ($maxAmplitudeSoundLastRecording > 11000) )
            #if ( motionDetected or ($maxAmplitudeSoundLastRecording > 12500) )
            if ( motionDetected )
                $interestingEventDetectedNumTimes += 1
                puts "Interesting event detected!!: interestingEventDetectedNumTimes = %d" % $interestingEventDetectedNumTimes
                $stdout.flush()

                #sleep(5.0)
                if (false)
                    #Doesn't work very well (basically I need to ~press Return key and then it doesn't come back, which makes it not SaveFrame() ).
                    #This works only once and I was not able to make the app return from the Dialer activity back to the activity with the Viewfinder.
                    $camera_demo.PhoneCall()
                end

                #!!!!Do a get to http://mobile-revival.110mb.com/sendemail.php, etc.

                $interestingEventDetected = true
                $lastTimeInterestingEventDetected = time #Time.new()
            else
                $interestingEventDetected = false
            end
        else
            $interestingEventDetected = true #This is under the "influence" of the event detected at $lastTimeInterestingEventDetected.
        end

        if ($interestingEventDetected == true) #This is either the first detection of an interesting event, either one of the frames taken in the following 10 seconds.
            #imgFileName = "#{Dir.pwd}/img"
            imgFileName = crtLOCAL_FOLDER_MEDIA_FILES + "/" #+ "/img"
            imgFileName += "%04d_%02d_%02d_%02d_%02d_%02d_%03d_%05d" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000, $maxAmplitudeSoundLastRecording]

            if ($lastTimeInterestingEventDetected == time) #This is the first detection of an interesting event
            #if (($interestingEventDetected == 1) and ($lastTimeInterestingEventDetected == time))
                imgFileName += "_%03d_%03d_%03d.jpg" % [avgDeltaR, avgDeltaG, avgDeltaB]
                #imgFileName += "!first"
            else
                imgFileName += ".jpg" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000, $maxAmplitudeSoundLastRecording, avgDeltaR, avgDeltaG, avgDeltaB]
            end
            #imgFileName += "%02d_%02d_%02d_%03d.jpg" % [time.hour, time.min, time.sec, time.usec / 1000]
            #puts "imgFileName = %s" % imgFileName
            #puts $mPreviewSize

            #imgFileName = "#{Dir.pwd}/img%02d_%02d_%02d_%06d.jpg" % [time.hour, time.min, time.sec, time.usec]
            #puts imgFileName
            #puts $mPreviewSize.width
            #puts $mPreviewSize.height

            #This works for API Level 8+
            fos = java.io.FileOutputStream.new(imgFileName)

            #See https://developer.android.com/reference/android/graphics/YuvImage.html and https://developer.android.com/reference/android/graphics/ImageFormat.html#NV21
            #See also https://code.google.com/p/android-scripting/source/browse/android/WebCamFacade/src/com/googlecode/android_scripting/facade/WebCamFacade.java?r=3e3e4e380197673000cbf370556a790442980c00
            #!!!!Use $mPreviewSize.width, $mPreviewSize.height instead - need to make mPreviewSize global
            #public YuvImage (byte[] yuv, int format, int width, int height, int[] strides)
            #yuvImageObj = YuvImage.new(crtFrame, 0x00000011, $mPreviewSize.width, $mPreviewSize.height, nil) #!!!!try to use android.graphics.ImageFormat::NV21 instead of 0x00000011
            yuvImageObj = YuvImage.new(crtFrame, android.graphics.ImageFormat::NV21, $mPreviewSize.width, $mPreviewSize.height, nil)

            #fos2 = java.io.FileOutputStream.new("#{Dir.pwd}/Am_here2")
            rectObj = Rect.new(0, 0, $mPreviewSize.width, $mPreviewSize.height)
            #fos3 = java.io.FileOutputStream.new("#{Dir.pwd}/Am_here3")
            mJpegQuality = 80 #between 0-100
            #public boolean compressToJpeg (Rect rectangle, int quality, OutputStream stream)
            yuvImageObj.compressToJpeg(rectObj, mJpegQuality, fos)
            #fos4 = java.io.FileOutputStream.new("#{Dir.pwd}/Am_here4")

            fos.close()
        end

        if ($USE_JPEG_FILESIZE)
            $JPEGfileSizeLastPhotoOld = $JPEGfileSizeLastPhoto
            $JPEGfileSizeLastPhoto = File.size(imgFileName)

            $JPEGfileSizeVariation = (($JPEGfileSizeLastPhotoOld - $JPEGfileSizeLastPhoto + 0.0001) / ($JPEGfileSizeLastPhoto + 0.0001))

            #puts "TakePictureCallback: Variation in JPEG file size = %f" % (($JPEGfileSizeLastPhotoOld - $JPEGfileSizeLastPhoto + 0.0001) / $JPEGfileSizeLastPhoto)
            puts "TakePictureCallback: $JPEGfileSizeLastPhotoOld = %d. $JPEGfileSizeLastPhoto = %d. Variation in JPEG file size = %f" % [$JPEGfileSizeLastPhotoOld, $JPEGfileSizeLastPhoto, $JPEGfileSizeVariation]
            $stdout.flush()
        end
        #We don't use File.new because it is very slow - don't understand why no IO buffering seems to be done.
        #f = File.new("Alex_test%d.nv21" % ($myCounter - 1), "wb")

##=begin
#       #In case we have API Level 7 or smaller
#       #imgFileName = "#{Dir.pwd}/FOSAlex_test%d.nv21" % ($myCounter - 1)
#       imgFileName = "#{Dir.pwd}/img%02d_%02d_%02d_%06d.nv21" % [time.hour, time.min, time.sec, time.usec]
#       fos = java.io.FileOutputStream.new(imgFileName)
#       #puts imgFileName
#       fos.write(crtFrame)
#       fos.close()
##=end
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end


#mPreviewCallback = PreviewCallback().new(self) do
class PreviewCallback
    #def on_surface_created(holder)
    def onPreviewFrame(crtFrame, camera) #From https://developer.android.com/reference/android/hardware/Camera.PreviewCallback.html: public abstract void onPreviewFrame (byte[] data, Camera camera)
        begin
            puts "Entered onPreviewFrame(): myCounter = %d" % $myCounter
            $stdout.flush()

            $myCounter += 1

            #if (($myCounter >= 10) && ($myCounter <= 9))
            #if (($myCounter >= 10) && ($myCounter <= 15))
            #if ( ($myCounter % 10) == 0) #On Prestigio, with NV21, it saves a photo every 2 seconds
            #if ( ($myCounter % 5) == 0) #On Prestigio, with JPEG, it saves a photo every 12/8 seconds
            #if ( ($myCounter % 4) == 0) #On Prestigio, with JPEG, it saves a photo every 86/71 seconds. Also 973 JPEGs / 1606 seconds = 0.606 JPEGs / second
            #if (true) #On Prestigio, with JPEG, it saves 9139 - 55' 31" = 3331 secs = 2.74 JPEGs / second.
            if ( ($myCounter % 2) == 0) #On Prestigio, with JPEG, it saves 4748 JPEGs / 3600 seconds = 1.32 JPEGs / second (another run: 6050 JPEGs / 3600 seconds = 1.68)
                SaveFrame(crtFrame)
            end

            #if ( ($myCounter % 40) == 1) #hopefully 860/71 seconds
            #    RecordAudioStart()
            #end
            #if ( ($myCounter % 40) == 39) #hopefully 860/71 seconds
            #    RecordAudioStop()
            #end

            #RecordAudio("/sdcard/extsd/iCam/iCamTemp.amr", 10.0)

            #maybe do something with $camera
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end
    end
end


def MyOpenCV
    #OpenCV related code

    begin
        puts "Entered MyOpenCV()"
        frame = org.opencv.core.Mat.new() #I get exception: "cannot load Java class org.opencv.core.Mat
        #org/jruby/javasupport/JavaUtilities.java:54:in `get_proxy_or_package_under_package'file:/data/app/org.ruboto.core-1.apk!/builtin/javasupport/java.rb:51:in `method_missing'demo-camera.rb:217:in `MyOpenCV'demo-camera.rb:267:in `on_surface_created'org/jruby/RubyMethod.java:134:in `call'"

        puts "MyOpenCV(): Am here 1"
        frame = Mat.new()

        puts "MyOpenCV(): Am here 2"
        aCapture = VideoCapture.new(0)
        puts "Exiting MyOpenCV()"
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end


def SetCameraParameters(camera)
    begin
        mParameters = camera.parameters #In Java is camera.getParameters()
        supportedPreviewSizes = mParameters.supported_preview_sizes #getSupportedPreviewSizes()

        puts "supportedPreviewSizes ="
        for aSizeElement in supportedPreviewSizes
            #print(aSizeElement) #This prints the object pointer

            #See https://developer.android.com/reference/android/hardware/Camera.Size.html
            puts "%d x %d" % [aSizeElement.width, aSizeElement.height]
        end

        #From https://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPictureFormats%28%29
        supportedPictureFormats = mParameters.supported_picture_formats #getSupportedPictureFormats()
        supportedPreviewFormats = mParameters.supported_preview_formats #getSupportedPreviewFormats()

        #See https://developer.android.com/reference/android/graphics/ImageFormat.html for details on the constants
        puts "supportedPictureFormats ="
        for anInteger in supportedPictureFormats
            puts anInteger #On Prestigio returns 256 (i.e., JPEG)
        end

        #See https://developer.android.com/reference/android/graphics/ImageFormat.html for details on the constants
        puts "supportedPreviewFormats ="
        for anInteger in supportedPreviewFormats
            puts anInteger #On Prestigio returns 17 (i.e., NV21)
        end

        #See https://developer.android.com/reference/android/hardware/Camera.Parameters.html#getPreviewFormat%28%29:
        #puts "camera.preview_format = %d" % camera.preview_format
        puts "mParameters.preview_format = %d" % mParameters.preview_format #Normally it is 17 (i.e., NV21)

        #!!!!One should check what are the supported preview formats 
        # and choose mParameters.setPreviewFormat(ImageFormat.JPEG or ImageFormat.NV21, etc) depending on the supported formats.
        #In fact the spec https://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewFormats%28%29 says "Gets the supported preview formats. NV21 is always supported. YV12 is always supported since API level 12."

        #See https://developer.android.com/reference/android/hardware/Camera.Parameters.html#getPreviewSize%28%29:
        $mPreviewSize = mParameters.preview_size
        puts "mPreviewSize = %d x %d" % [$mPreviewSize.width, $mPreviewSize.height] #Normally it is 640 x 480
        #puts "camera.preview_size = %d x %d" % [camera.preview_size.width, camera.preview_size.height]

        $rgb0 = Array.new($mPreviewSize.width * $mPreviewSize.height / ($CONVERT_DELTA_X * $CONVERT_DELTA_Y)){0x00000000}
        $rgb2 = Array.new($mPreviewSize.width * $mPreviewSize.height / ($CONVERT_DELTA_X * $CONVERT_DELTA_Y)){0x00000000}

        puts "Initially, mParameters.flash_mode = %s" % mParameters.flash_mode

        #See https://developer.android.com/reference/android/hardware/Camera.Parameters.html#FLASH_MODE_OFF
        #mParameters.setFlashMode("off")
        mParameters.flash_mode = "off" #Somehow it blocks the preview in take_photo()
        puts "After setting it, mParameters.flash_mode = %s" % mParameters.flash_mode
        #mParameters.flash_mode = mParameters.FLASH_MODE_OFF #Exception: "NoMethodError: undefined method `FLASH_MODE_OFF' for
        #maNewParameters = Camera.Parameters.new()
        #https://developer.android.com/reference/android/hardware/Camera.html#setParameters%28android.hardware.Camera.Parameters%29: public void setParameters (Camera.Parameters params)
        #camera.setParameters(Camera.Parameters params)
        camera.parameters = mParameters
        #puts "Finished disabling the Flash"
    rescue => exc
        #p exc.message
        warn exc.message()
        warn exc.backtrace()
        #$stdout.flush()
        $stderr.flush()
    end
end



################################################################################
################################################################################
################################################################################
################################################################################
################################################################################
###################This is the Acitivity of the application#####################
##################(the main entry point of the application)#####################
################################################################################
################################################################################
################################################################################
################################################################################
# For Ruboto v0.7: $activity.start_ruboto_activity "$camera_demo", RubotoActivity, R.style::Theme_NoTitleBar_Fullscreen do
# For Ruboto v0.8:
# Note: all class names have to start with capital letter
class ICamTest
    def PhoneCall
        begin
            puts "Entered PhoneCall()."
            $stdout.flush()

            intent = Intent.new(Intent::ACTION_CALL)
            #intent.setData(Uri.parse("tel:0318041336"))
            intent.setData(Uri.parse("tel:+40771234567"))
            startActivity(intent)
            #sleep(2.0)
            #stopActivity()

            #See https://github.com/ruboto/ruboto/wiki/Using-Activities-and-Intents
            $activity.start_ruboto_activity("$camera_demo")
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end
    end

    def on_create(bundle)
        begin
            super

            freeSpaceLocalDrive = GetFreeDriveSpace($LOCAL_DRIVE)
            puts "freeSpaceLocalDrive = %d" % freeSpaceLocalDrive
            if (freeSpaceLocalDrive < 2.0 * 1024 * 1024 * 1024)
                myStr = "Warning: local drive has only %d MB free space." % (freeSpaceLocalDrive / (1024 * 1024))
                for myRepeat in (0..1)
                    #Using toast without return doesn't show the toast message... However the display is completely black until we get out of the loop.
                    toast myStr
                    puts myStr
                    sleep(4.0)
                end
                #return
            end

            #Ruboto v0.7: @surface_view = SurfaceView.new(self)
            #Ruboto v0.8:
            @surface_view = android.view.SurfaceView.new(@ruboto_java_instance)

            #See https://developer.android.com/reference/android/view/View.html: void   setOnClickListener(View.OnClickListener l)
            #See https://developer.android.com/reference/android/view/View.OnClickListener.html: View.OnClickListener is an interface with one method: abstract void    onClick(View v), where v is "The view that was clicked."
            #IMPORTANT NOTE: the name of the function passed to set_on_click_listener has to be take_picture - something else will crash Ruboto IRB
            #Ruboto v0.7: @surface_view.set_on_click_listener(proc{|v| take_picture})
            #Ruboto v0.8:
            @surface_view.set_on_click_listener{|v| take_picture}

            #Ruboto v0.8:
            @holder_callback = RubotoSurfaceHolderCallback.new

            @surface_view.holder.add_callback @holder_callback #See https://developer.android.com/reference/android/view/SurfaceHolder.html#addCallback(android.view.SurfaceHolder.Callback)

            # Deprecated, but still required for older API version
            @surface_view.holder.set_type android.view.SurfaceHolder::SURFACE_TYPE_PUSH_BUFFERS
            self.content_view = @surface_view

            begin
                wifi_service = getSystemService(android.content.Context::WIFI_SERVICE)
                ssid = wifi_service.connection_info.getSSID()
                puts "ssid = %s" % ssid
            rescue => exc
                #p exc.message
                warn exc.message()
                warn exc.backtrace()
                #$stdout.flush()
                $stderr.flush()
            end
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end
    end

    def TakePictureCallback(data, camera)
        begin
            #From http://www.tutorialspoint.com/ruby/ruby_date_time.htm
            #puts "TakePictureCallback: %02d_%02d_%02d_%06d" % [time.hour, time.min, time.sec, time.usec]
            puts "TakePictureCallback: %04d_%02d_%02d_%02d_%02d_%02d_%03d.jpg" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

            #picture_file = "#{Dir.pwd}/picture#{$camera.picture_id}.jpg"
            #picture_file = "#{Dir.pwd}/picture#{$camera.picture_id}"
            picture_file = $LOCAL_FOLDER_MEDIA_FILES + "/pic"
            picture_file += "%04d_%02d_%02d_%02d_%02d_%02d_%03d.jpg" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

            fos = java.io.FileOutputStream.new(picture_file)
            fos.write(data)
            fos.close()
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end
    end
    #myNewFunc = method(:TakePictureCallback) #uninitialized constant TestFunc (NameError)
    #myNewFunc.call(1234)


    def take_picture
        #RecordAudio("/sdcard/extsd/iCam/iCamTemp.amr", 10.0)
        #RecordVideo("/sdcard/extsd/iCam/iCamTemp.3gp", 5.0)

        return

        begin
            #From http://www.tutorialspoint.com/ruby/ruby_date_time.htm
            #puts "take_picture: %02d_%02d_%02d_%06d" % [time.hour, time.min, time.sec, time.usec]
            puts "take_picture: %04d_%02d_%02d_%02d_%02d_%02d_%03d.jpg" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

            #picture_file = "#{Dir.pwd}/picture#{$camera.picture_id}.jpg"
            picture_file = "#{Dir.pwd}/picture#{$camera.picture_id}"
            picture_file += "%04d_%02d_%02d_%02d_%02d_%02d_%03d.jpg" % [time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec / 1000]

            #public final void takePicture (Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg)
            puts "Preparing to add callbacks for camera.take_picture"
            #$camera.take_picture(nil, method(:TakePictureCallback).call, nil) #This makes the IRB crash: "Sorry! The application Ruboto IRB (process org.ruboto.irb) has stopped unexpectedly. Please try again.")
            #$camera.take_picture(nil, method(:TakePictureCallback), nil) #This gets executed, but the registered raw callback doesn't get executed.

            #$camera.take_picture(nil, proc{|data, camera| TakePictureCallback}, nil) #This gets executed, but the registered raw callback doesn't get executed.

            myFuncVar = Proc.new do |data, camera| TakePictureCallback
            end

=begin
            $camera.take_picture(nil) do |data, camera|
                #This anonymous function is the RAW callback
                puts "Entered RAW callback"
            end
=end

            puts "Added callbacks for camera.take_picture"

=begin
            #Original code had a toast: $camera.take_picture(proc{toast "Picture taken"}, nil) do |data, camera|
            #public final void takePicture (Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg)
            $camera.take_picture(nil, nil) do |data, camera|
                #This anonymous function is the JPEG callback

                #!!!!TODO Implement the raw callback - for faster processing, since we don't need to do JPEG compression anymore.

                #fos = java.io.FileOutputStream.new(picture_file)
                #fos.write(data)
                #fos.close()

                #Restart the preview, in order to take_picture again
                #See https://developer.android.com/reference/android/hardware/Camera.html#setOneShotPreviewCallback%28android.hardware.Camera.PreviewCallback%29
                #"After calling this method, you must not call startPreview() or take another picture until the JPEG callback has returned."
                #IMPORTANT: Starting the preview fails normally - I guess I have to do it outside the callback - !!!!create a thread maybe that executes after a few millisecs $camera.start_preview
                #IMPORTANT: As expected, when I put $camera.start_preview and take_picture outside the callback it blocks, since I am starting $camera.start_preview maybe even before the callback was called.
                $camera.start_preview
                take_picture
            end
=end
        rescue => exc
            #p exc.message
            warn exc.message()
            warn exc.backtrace()
            #$stdout.flush()
            $stderr.flush()
        end

        #We don't need to return any result
        #picture_file
        #return picture_file
    end
end

# For Ruboto v0.8:
$irb.start_ruboto_activity "$camera_demo", RubotoActivity, R.style::Theme_NoTitleBar_Fullscreen, 
                             :class_name => "ICamTest"
